package com.audaque.springboot.foshanupload.jwtcore.component;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;

import cn.hutool.jwt.signers.AlgorithmUtil;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaPem;
import com.audaque.springboot.foshanupload.jwtcore.util.encode.rsaPem.RsaUtil;
import com.audaque.springboot.foshanupload.jwtcore.component.iface.JwtProviderIface;


import com.audaque.springboot.foshanupload.jwtcore.constants.JwtConst;
import com.audaque.springboot.foshanupload.jwtcore.properties.JwtProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

/**
 * @author zgb
 * @desc ...
 * @date 2022-06-29 20:50:13
 */
@ConditionalOnExpression("${switch.jJWTProvider}")
@Component
@Slf4j
public class JJWTProvider implements JwtProviderIface {
    @Autowired
    private JwtProperties jwtProperties;



    public String generateKeyStr() {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);//或HS384或HS512
        String format = secretKey.getFormat();
        byte[] encoded = secretKey.getEncoded();
        String s1 = Base64.getEncoder().encodeToString(secretKey.getEncoded());
        String algorithm = secretKey.getAlgorithm();
        String s = secretKey.toString();
        return s1;
    }

    public SecretKey generateKey() {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);//或HS384或HS512
        return secretKey;
    }



    public KeyPair generateKeyPair() {
        String algorithmId = jwtProperties.getAlgorithmId();
        //对称加密
        String algorithm = AlgorithmUtil.getAlgorithm(algorithmId);
        KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
        return keyPair;
    }







    public String getAccessToken(Map<String, Object> map) throws Exception {
        //加密生成token
        String algorithmId = jwtProperties.getAlgorithmId();
        String skey = jwtProperties.getSkey();
        String privateKey = jwtProperties.getPrivateKey();
        String publicKey = jwtProperties.getPublicKey();
        Map<String, Object> headers = new HashMap<>();
        Map<String, Object> playloads = new HashMap<>();
        playloads = map;


        String issuer = jwtProperties.getIssuer();
        Long expires = jwtProperties.getExpires();
        long current = DateUtil.current();
        DateTime currentDate = DateUtil.date(current);
        expires += current;
        DateTime expiresAt = DateUtil.date(expires);
        JwtBuilder builder = Jwts.builder()
                .setHeader(headers)
                .setClaims(playloads)
                .setExpiration(expiresAt)
                .setIssuer(issuer)
                .setIssuedAt(currentDate)
                .setNotBefore(currentDate)
                .setSubject("access token");
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.valueOf(algorithmId);
        byte[] privateKeyDecoded=null;
        List<String> skeyAlgorithmIdList = JwtConst.SKEY_ALGORITHM_ID_LIST;
        boolean contains = skeyAlgorithmIdList.contains(algorithmId);
        if (contains) {
            //对称加密
            //"none","HS256","HS384","HS512"
            builder.signWith(signatureAlgorithm, skey);//设置签名使用的签名算法和签名使用的秘钥
            String accessToken = builder.compact();
            return accessToken;
        } else if (StringUtils.isNotBlank(privateKey) && StringUtils.isNotBlank(publicKey)) {
            //非对称加密
     /*     privateKey = StringUtils.remove(privateKey, "-----BEGIN RSA PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----END RSA PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----BEGIN PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----END PRIVATE KEY-----");*/
            //用PEM格式密钥对创建RSA，支持PKCS#1、PKCS#8格式的PEM
            RsaUtil rsa = new RsaUtil(RsaPem.FromPEM(privateKey));
            RSAPrivateKey rSAPrivateKey = rsa.privateKey;
            builder.signWith( rSAPrivateKey,signatureAlgorithm);//设置签名使用的签名算法和签名使用的秘钥
            String accessToken = builder.compact();
            return accessToken;
        }else{
            return null;
        }


    }

    public String getBody(String accessToken) throws Exception {
        Jws<Claims> jwt=this.getJwt( accessToken);
        Claims body = jwt.getBody();
        String s = JSONUtil.toJsonStr(body);
        return s;
    }

    private Jws<Claims> getJwt(String accessToken) throws Exception {
        String algorithmId = jwtProperties.getAlgorithmId();
        String skey = jwtProperties.getSkey();
        String privateKey = jwtProperties.getPrivateKey();
        String publicKey = jwtProperties.getPublicKey();
        boolean b = this.verifyJwtStr(accessToken);
        if (!b) {
            return null;
        }


        //得到DefaultJwtParser
        JwtParser parser = Jwts.parser();

        List<String> skeyAlgorithmIdList = JwtConst.SKEY_ALGORITHM_ID_LIST;
        boolean contains = skeyAlgorithmIdList.contains(algorithmId);

        if (contains) {
            //对称解密
            //"none","HS256","HS384","HS512"
            //设置签名的秘钥
            parser.setSigningKey(skey);
        } else if (StringUtils.isNotBlank(privateKey) && StringUtils.isNotBlank(publicKey)) {
            //非对称解密
    /*        publicKey = StringUtils.remove(publicKey, "-----BEGIN RSA PUBLIC KEY-----");
            publicKey = StringUtils. remove(publicKey, "-----END RSA PUBLIC KEY-----");
            publicKey = StringUtils. remove(publicKey, "-----BEGIN PUBLIC KEY-----");
            publicKey = StringUtils. remove(publicKey, "-----END PUBLIC KEY-----");

            privateKey = StringUtils.remove(privateKey, "-----BEGIN RSA PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----END RSA PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----BEGIN PRIVATE KEY-----");
            privateKey = StringUtils. remove(privateKey, "-----END PRIVATE KEY-----");

*/
            RsaUtil rsa = new RsaUtil(RsaPem.FromPEM(privateKey));
            RSAPublicKey rSAPublicKey = rsa.publicKey;
            //设置签名的秘钥
            parser.setSigningKey(rSAPublicKey);
        }

        Jws<Claims> claimsJws =
                parser
                .requireSubject("access token") //判断主题
                .require("verify", "verify")//判断claims是否包含key-value
                .setAllowedClockSkewSeconds(3*60)//判断时钟偏移
                .parseClaimsJws(accessToken);
        b = this.verifyJwt(claimsJws);
        if (!b) {
            return null;
        }
        return claimsJws;
    }


    private boolean verifyJwtStr(String accessToken) {
        return true;

    }


    private boolean verifyJwt(Jws<Claims>  jwt ) {
        JwsHeader header = jwt.getHeader();
        String signature = jwt.getSignature();
        Claims body = jwt.getBody();

        String algorithm = header.getAlgorithm();
        String algorithmId = jwtProperties.getAlgorithmId();
        String type = header.getType();
        String keyId = header.getKeyId();
        String compressionAlgorithm = header.getCompressionAlgorithm();
        String contentType = header.getContentType();
        if(!Objects.equals(algorithmId,algorithm)){
           return false;
        }
        String issuer = body.getIssuer();
        Date expiration = body.getExpiration();
        Date issuedAt = body.getIssuedAt();
        Date notBefore = body.getNotBefore();
        String issuer1 = jwtProperties.getIssuer();

        Date now = DateUtil.date(DateUtil.current());
        long between1 = DateUtil.between(expiration, now, DateUnit.MS);
        long between2 = DateUtil.between(issuedAt, now, DateUnit.MS);
        long between3 = DateUtil.between(notBefore, now, DateUnit.MS);
        if(!Objects.equals(issuer,issuer1)||!(between1>0L)||!(between2>0L)||!(between3>0L)){
            return false;
        }

        return true;

    }






}


